using System;
using System.Collections.Generic;
using Oracle.ManagedDataAccess.Types;
using OracleInternal.I18N;
using OracleInternal.ServiceObjects;
using OracleInternal.TTC.Accessors;

namespace OracleInternal.TTC
{
	internal class TTCXmlTypePickler
	{
		internal const string TypeName = "SYS.XMLTYPE";

		internal static byte[] TOID = new byte[16]
		{
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			0,
			2,
			1,
			0
		};

		internal const int XMLTYPE_VERSION = 1;

		internal const int XMLTYPE_LOB = 1;

		internal const int XMLTYPE_OBJECT = 2;

		internal const int XMLTYPE_STR = 4;

		internal const int XMLTYPE_PTR = 8;

		internal const int XMLTYPE_CSX = 16777216;

		internal const int XMLTYPE_LOB_CSX = 16777217;

		internal const int XMLTYPE_XQ_SEQ_DMFMT = 524288;

		internal const uint XMLTYPE_FLAG_SKIP_NEXT_4 = 1048576u;

		internal const uint XMLTYPE_FLAG_SNAPSHOT = 4096u;

		internal const uint XMLTYPE_FLAG_SCHMOID = 8u;

		internal const uint XMLTYPE_FLAG_NO_DOC_WRAP = 4194304u;

		internal const uint XMLTYPE_FLAG_FRAGMENT = 32u;

		internal const uint XMLTYPE_FLAG_NOXMLHDR = 1024u;

		internal const uint XMLTYPE_FLAG_GUID = 536870912u;

		internal const uint XMLTYPE_FLAG_CSID = 2147483648u;

		internal const int XMLTYPE_SCHMOIDLEN = 16;

		internal const int XMLTYPE_ELEMNUMLEN = 4;

		internal const int XMLTYPE_KOSNPLEN = 34;

		internal const int XMLTYPE_KOSNPLEN_OLD = 24;

		internal const int XMLTYPE_GUIDLEN = 16;

		internal const int XMLTYPE_CSID_SIZE = 2;

		internal const short KOPI20_IF_IS81 = 128;

		internal const short KOPI20_IF_CMSB = 64;

		internal const short KOPI20_IF_CLSB = 32;

		internal const short KOPI20_IF_DEGN = 16;

		internal const short KOPI20_IF_COLL = 8;

		internal const short KOPI20_IF_NOPS = 4;

		internal const short KOPI20_IF_ANY = 2;

		internal const short KOPI20_IF_NONL = 1;

		internal const short KOPI20_VERSION = 1;

		internal const short KOPI20_LN_MAXV = 245;

		internal const short KOPI20_LN_5BLN = 254;

		internal static long ReadIntInNativeFormat(byte[] buf)
		{
			return 0L + (long)(buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3] & 0xFF);
		}

		internal static int ReadShortInNativeFormat(byte[] buf)
		{
			return 0 + (buf[0] << 8) + (buf[1] & 0xFF);
		}

		internal static int WriteInt2Bytes(byte[] bytes, int index, int val)
		{
			bytes[index++] = (byte)(val >> 24);
			val &= 0xFFFFFF;
			bytes[index++] = (byte)(val >> 16);
			val &= 0xFFFF;
			bytes[index++] = (byte)(val >> 8);
			val &= 0xFF;
			bytes[index++] = (byte)val;
			return 4;
		}

		internal static int WriteShort2Bytes(byte[] bytes, int index, int val)
		{
			bytes[index++] = (byte)(val >> 8);
			val &= 0xFF;
			bytes[index++] = (byte)val;
			return 2;
		}

		private static int WriteLength(byte[] bytes, int index, long dataLength)
		{
			if (dataLength <= 245)
			{
				bytes[index] = (byte)dataLength;
				return 1;
			}
			bytes[index++] = 254;
			bytes[index++] = (byte)(dataLength >> 24);
			dataLength &= 0xFFFFFF;
			bytes[index++] = (byte)(dataLength >> 16);
			dataLength &= 0xFFFF;
			bytes[index++] = (byte)(dataLength >> 8);
			dataLength &= 0xFF;
			bytes[index++] = (byte)dataLength;
			return 5;
		}

		private static int WriteBuffer(byte[] destBytes, byte[] srcBytes, int destIndex, int srcIndex, long dataLength)
		{
			int num = (int)dataLength;
			if (srcBytes != null && srcBytes.Length < dataLength)
			{
				num = srcBytes.Length;
			}
			for (int i = 0; i < num; i++)
			{
				destBytes[destIndex++] = srcBytes[srcIndex++];
			}
			return num;
		}

		internal static void GetXmlHeaderLength(OracleXmlTypeImpl xmlTypeImpl, out int length)
		{
			length = 5;
			if ((xmlTypeImpl.m_xmlFlag & 0x1000) == 4096 && xmlTypeImpl.m_snapshot != null)
			{
				length += xmlTypeImpl.m_kpsnpLen;
			}
			if ((xmlTypeImpl.m_xmlFlag & 8) == 8 && xmlTypeImpl.m_schemaID != null && xmlTypeImpl.m_schElem != null)
			{
				length += 20;
			}
		}

		internal static int ReadNamedTypeHeader(List<ArraySegment<byte>> dataSegments, byte[] buf, ref int pos, OraXmlTypeHeader xmlTypeHeader)
		{
			int num = 0;
			xmlTypeHeader.m_dataLength = 0L;
			pos++;
			num++;
			pos++;
			num++;
			Accessor.CopyDataToUserBuffer(dataSegments, pos, buf, 0, 1);
			xmlTypeHeader.m_dataLength = buf[0] & 0xFF;
			pos++;
			num++;
			if (xmlTypeHeader.m_dataLength > 245)
			{
				Accessor.CopyDataToUserBuffer(dataSegments, pos, buf, 0, 4);
				xmlTypeHeader.m_dataLength = (((buf[0] & 0xFF) * 256 + (buf[1] & 0xFF)) * 256 + (buf[2] & 0xFF)) * 256 + (buf[3] & 0xFF);
				pos += 4;
				num += 4;
			}
			return num;
		}

		internal static bool ReadXmlHeader(OracleConnectionImpl connImpl, List<ArraySegment<byte>> dataSegments, OraXmlTypeHeader xmlTypeHeader)
		{
			byte[] array = new byte[64];
			int pos = 0;
			int num = ReadNamedTypeHeader(dataSegments, array, ref pos, xmlTypeHeader);
			pos++;
			num++;
			Accessor.CopyDataToUserBuffer(dataSegments, pos, array, 0, 4);
			uint num2 = (uint)ReadIntInNativeFormat(array);
			pos += 4;
			num += 4;
			if ((num2 & 0x100000) == 1048576)
			{
				pos += 4;
				num += 4;
			}
			if ((num2 & 0x1000) == 4096)
			{
				int kpsnpLen = OracleXmlTypeImpl.GetKpsnpLen(connImpl);
				xmlTypeHeader.m_snapshot = new byte[kpsnpLen];
				Accessor.CopyDataToUserBuffer(dataSegments, pos, xmlTypeHeader.m_snapshot, 0, kpsnpLen);
				pos += kpsnpLen;
				num += kpsnpLen;
				xmlTypeHeader.m_xmlFlag |= 4096u;
			}
			if ((num2 & 8) == 8)
			{
				xmlTypeHeader.m_schoid = new byte[16];
				Accessor.CopyDataToUserBuffer(dataSegments, pos, xmlTypeHeader.m_schoid, 0, 16);
				pos += 16;
				num += 16;
				xmlTypeHeader.m_schElem = new byte[4];
				Accessor.CopyDataToUserBuffer(dataSegments, pos, xmlTypeHeader.m_schElem, 0, 4);
				pos += 4;
				num += 4;
				xmlTypeHeader.m_xmlFlag |= 8u;
			}
			if ((num2 & 0x20000000) == 536870912)
			{
				pos += 16;
				num += 16;
				xmlTypeHeader.m_xmlFlag |= 536870912u;
			}
			if ((num2 & 0x80000) != 524288)
			{
				if ((num2 & 1) == 1)
				{
					if ((num2 & 0x80000000u) == 2147483648u)
					{
						xmlTypeHeader.m_typeOfXmlType = TypeOfXmlType.BlobWithText;
					}
					else if ((num2 & 0x1000000) == 16777216)
					{
						xmlTypeHeader.m_typeOfXmlType = TypeOfXmlType.BlobCSX;
					}
					else
					{
						xmlTypeHeader.m_typeOfXmlType = TypeOfXmlType.Clob;
					}
				}
				else if ((num2 & 4) == 4)
				{
					xmlTypeHeader.m_typeOfXmlType = TypeOfXmlType.String;
				}
				else if ((num2 & 2) == 2)
				{
					xmlTypeHeader.m_typeOfXmlType = TypeOfXmlType.Object;
				}
			}
			xmlTypeHeader.m_headerLength = num;
			if (xmlTypeHeader.m_dataLength > 0)
			{
				xmlTypeHeader.m_dataLength -= xmlTypeHeader.m_headerLength;
			}
			return true;
		}

		internal static long GetLengthAndFlag(Conv dbCharSetConv, OracleXmlTypeImpl xmlTypeImpl, TypeOfXmlData typeOfXmlData, object xmlData, ref uint xmlFlag)
		{
			int length = 0;
			long num = 0L;
			long num2 = 0L;
			xmlFlag = xmlTypeImpl.m_xmlFlag | (uint)xmlTypeImpl.m_typeOfXmlType;
			GetXmlHeaderLength(xmlTypeImpl, out length);
			switch (typeOfXmlData)
			{
			case TypeOfXmlData.String:
			{
				string text = (string)xmlData;
				num2 = dbCharSetConv.GetBytesLength(text, 0, text.Length);
				break;
			}
			case TypeOfXmlData.Chars:
			{
				char[] array = (char[])xmlData;
				num2 = dbCharSetConv.GetBytesLength(array, 0, array.Length);
				break;
			}
			case TypeOfXmlData.Clob:
				num2 = ((OracleClob)xmlData).m_clobImpl.m_lobLocator.Length;
				break;
			case TypeOfXmlData.BlobWithText:
				num2 = ((OraXmlBlobWithText)xmlData).m_xmlBlob.m_blobImpl.m_lobLocator.Length + 2;
				break;
			case TypeOfXmlData.BlobCSX:
				num2 = ((OracleBlob)xmlData).m_blobImpl.m_lobLocator.Length;
				break;
			}
			num += 2;
			num += length;
			num += num2;
			if (num <= 244)
			{
				return num + 1;
			}
			return num + 5;
		}

		internal static long GetLengthAndFlag(Conv dbCharSetConv, TypeOfXmlType typeOfXmlType, TypeOfXmlData typeOfXmlData, int offset, int size, object xmlData, ref uint xmlFlag)
		{
			int num = 5;
			long num2 = 0L;
			long num3 = 0L;
			xmlFlag = (uint)typeOfXmlType;
			switch (typeOfXmlData)
			{
			case TypeOfXmlData.String:
			{
				string str = (string)xmlData;
				num3 = dbCharSetConv.GetBytesLength(str, offset, size);
				break;
			}
			case TypeOfXmlData.Chars:
			{
				char[] chars = (char[])xmlData;
				num3 = dbCharSetConv.GetBytesLength(chars, offset, size);
				break;
			}
			case TypeOfXmlData.Clob:
				num3 = ((OracleClob)xmlData).m_clobImpl.m_lobLocator.Length;
				break;
			case TypeOfXmlData.BlobWithText:
				num3 = ((OraXmlBlobWithText)xmlData).m_xmlBlob.m_blobImpl.m_lobLocator.Length + 2;
				break;
			case TypeOfXmlData.BlobCSX:
				num3 = ((OracleBlob)xmlData).m_blobImpl.m_lobLocator.Length;
				break;
			}
			num2 += 2;
			num2 += num;
			num2 += num3;
			if (num2 <= 244)
			{
				return num2 + 1;
			}
			return num2 + 5;
		}

		internal static byte[] Pickle(Conv dbCharSetConv, OracleXmlTypeImpl xmlTypeImpl)
		{
			byte[] array = null;
			uint xmlFlag = 0u;
			int num = 0;
			TypeOfXmlData typeOfXmlData = TypeOfXmlData.NoData;
			object xmlData = null;
			xmlTypeImpl.GetXmlDataForPickling(out typeOfXmlData, out xmlData);
			long lengthAndFlag = GetLengthAndFlag(dbCharSetConv, xmlTypeImpl, typeOfXmlData, xmlData, ref xmlFlag);
			array = new byte[lengthAndFlag];
			array[num++] = 133;
			array[num++] = 1;
			num += WriteLength(array, num, lengthAndFlag);
			array[num++] = 1;
			num += WriteInt2Bytes(array, num, (int)xmlFlag);
			if ((xmlFlag & 0x1000) == 4096 && xmlTypeImpl.m_snapshot != null)
			{
				WriteBuffer(array, xmlTypeImpl.m_snapshot, num, 0, xmlTypeImpl.m_kpsnpLen);
				num += xmlTypeImpl.m_kpsnpLen;
			}
			if ((xmlFlag & 8) == 8 && xmlTypeImpl.m_schemaID != null && xmlTypeImpl.m_schElem != null)
			{
				WriteBuffer(array, xmlTypeImpl.m_schemaID, num, 0, 16L);
				num += 16;
				WriteBuffer(array, xmlTypeImpl.m_schElem, num, 0, 4L);
				num += 4;
			}
			switch (typeOfXmlData)
			{
			case TypeOfXmlData.String:
			{
				int byteCount2 = (int)lengthAndFlag;
				string text = (string)xmlData;
				dbCharSetConv.ConvertStringToBytes(text, 0, text.Length, array, num, ref byteCount2);
				break;
			}
			case TypeOfXmlData.Chars:
			{
				int byteCount = (int)lengthAndFlag;
				char[] array2 = (char[])xmlData;
				dbCharSetConv.ConvertCharsToBytes(array2, 0, array2.Length, array, num, ref byteCount);
				break;
			}
			case TypeOfXmlData.Clob:
			{
				OracleClob oracleClob = (OracleClob)xmlData;
				lengthAndFlag = oracleClob.m_clobImpl.m_lobLocator.Length;
				WriteBuffer(array, oracleClob.m_clobImpl.m_lobLocator, num, 0, lengthAndFlag);
				break;
			}
			case TypeOfXmlData.BlobWithText:
			{
				OracleBlob xmlBlob = ((OraXmlBlobWithText)xmlData).m_xmlBlob;
				WriteBuffer(array, xmlBlob.m_blobImpl.m_lobLocator, num, 0, xmlBlob.m_blobImpl.m_lobLocator.Length);
				num += xmlBlob.m_blobImpl.m_lobLocator.Length;
				WriteShort2Bytes(array, num, ((OraXmlBlobWithText)xmlData).m_csid);
				break;
			}
			case TypeOfXmlData.BlobCSX:
			{
				OracleBlob oracleBlob = (OracleBlob)xmlData;
				lengthAndFlag = oracleBlob.m_blobImpl.m_lobLocator.Length;
				WriteBuffer(array, oracleBlob.m_blobImpl.m_lobLocator, num, 0, lengthAndFlag);
				break;
			}
			}
			return array;
		}

		internal static byte[] Pickle(Conv dbCharSetConv, TypeOfXmlType typeOfXmlType, TypeOfXmlData typeOfXmlData, int offset, int size, object xmlData)
		{
			byte[] array = null;
			uint xmlFlag = 0u;
			int num = 0;
			long lengthAndFlag = GetLengthAndFlag(dbCharSetConv, typeOfXmlType, typeOfXmlData, offset, size, xmlData, ref xmlFlag);
			array = new byte[lengthAndFlag];
			array[num++] = 133;
			array[num++] = 1;
			num += WriteLength(array, num, lengthAndFlag);
			array[num++] = 1;
			num += WriteInt2Bytes(array, num, (int)xmlFlag);
			switch (typeOfXmlData)
			{
			case TypeOfXmlData.String:
			{
				int byteCount2 = (int)lengthAndFlag;
				string str = (string)xmlData;
				dbCharSetConv.ConvertStringToBytes(str, offset, size, array, num, ref byteCount2);
				break;
			}
			case TypeOfXmlData.Chars:
			{
				int byteCount = (int)lengthAndFlag;
				char[] chars = (char[])xmlData;
				dbCharSetConv.ConvertCharsToBytes(chars, offset, size, array, num, ref byteCount);
				break;
			}
			case TypeOfXmlData.Clob:
			{
				OracleClob oracleClob = (OracleClob)xmlData;
				int num2 = oracleClob.m_clobImpl.m_lobLocator.Length;
				WriteBuffer(array, oracleClob.m_clobImpl.m_lobLocator, num, 0, num2);
				break;
			}
			case TypeOfXmlData.BlobWithText:
			{
				OracleBlob xmlBlob = ((OraXmlBlobWithText)xmlData).m_xmlBlob;
				WriteBuffer(array, xmlBlob.m_blobImpl.m_lobLocator, num, 0, xmlBlob.m_blobImpl.m_lobLocator.Length);
				num += xmlBlob.m_blobImpl.m_lobLocator.Length;
				WriteShort2Bytes(array, num, ((OraXmlBlobWithText)xmlData).m_csid);
				break;
			}
			case TypeOfXmlData.BlobCSX:
			{
				OracleBlob oracleBlob = (OracleBlob)xmlData;
				lengthAndFlag = oracleBlob.m_blobImpl.m_lobLocator.Length;
				WriteBuffer(array, oracleBlob.m_blobImpl.m_lobLocator, num, 0, lengthAndFlag);
				break;
			}
			}
			return array;
		}
	}
}
